package promote.brozen.netty.jav.nio;

import org.apache.commons.collections4.CollectionUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Brozen
 * @date 2020/9/27 10:00 AM
 * @email brozen@qq.com
 */
public class NioClient {

    private AtomicReference<Thread> sinThreadRef = new AtomicReference<>(null);

    public static void main(String[] args) throws IOException {
        new NioClient().start();
    }

    public void start() throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);

//        Socket clientSocket = socketChannel.socket();
//        clientSocket.setReuseAddress(true);
//        clientSocket.setReceiveBufferSize();
//        clientSocket.setSendBufferSize();
//        clientSocket.connect();

        Selector selector = Selector.open();

        if (socketChannel.connect(new InetSocketAddress("127.0.0.1", 8090))) {
            socketChannel.register(selector, SelectionKey.OP_READ);
            startSINThread(socketChannel);
        } else {
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }

        while (true) {
            if (selector.select() < 0) {
                continue;
            }

            Set<SelectionKey> keys = selector.selectedKeys();
            if (CollectionUtils.isEmpty(keys)) {
                continue;
            }

            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (!key.isValid()) {
                    continue;
                }

                if (key.isConnectable()) {
                    SelectableChannel channel = key.channel();
                    SocketChannel sc = (SocketChannel) channel;
                    if (sc.finishConnect()) {
                        // 连接成功，启动线程读取信息发送给客户端
                        channel.register(selector, SelectionKey.OP_READ);
                        startSINThread(sc);
                    } else {
                        // 连接失败
                        System.out.println("连接失败！");
                        System.exit(-1);
                    }

                } else if (key.isReadable()) {
                    receiveFromServer(((SocketChannel) key.channel()));
                }
            }
        }

    }

    private void receiveFromServer(SocketChannel channel) {
        try {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int readBytes = channel.read(buffer);

            if (readBytes > 0) {
                buffer.flip();
                byte[] data = new byte[buffer.remaining()];
                buffer.get(data);
                System.out.println("ClientReceive: " + new String(data));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void startSINThread(SocketChannel channel) {
        Thread t = new Thread(() -> readAndSend(channel));
        if (sinThreadRef.compareAndSet(null, t)) {
            t.start();
        }
    }

    private void readAndSend(SocketChannel channel) {
        Scanner scanner = new Scanner(System.in);
        String line = null;
        while (!(line = scanner.nextLine()).equalsIgnoreCase("exit")) {
            try {
                if (!channel.isConnected()) {
                    System.out.println("Not connected!");
                    continue;
                }

                byte[] data = line.getBytes();
                ByteBuffer buffer = ByteBuffer.allocate(data.length);
                buffer.put(data);
                buffer.flip();
                channel.write(buffer);
                System.out.println("ClientSend: " + line);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}
